package main

import (
	"fmt"
	"time"
	"sync"
	"unsafe"
	"strconv"
)

var wg sync.WaitGroup

/**
支持过期时间和最大内存大小的的内存缓存库。
*/
type Cache interface {
	//size 是⼀一个字符串串。⽀支持以下参数: 1KB，100KB，1MB，2MB，1GB 等
	SetMaxMemory(size string) bool
	// 设置⼀一个缓存项，并且在expire时间之后过期
	Set(key string, val interface{}, expire time.Duration)
	// 获取⼀一个值
	Get(key string) (interface{}, bool)
	// 删除⼀一个值
	Del(key string) bool
	// 检测⼀一个值 是否存在
	Exists(key string) bool
	// 情况所有值
	Flush() bool
	// 返回所有的key 多少
	Keys() int64
}

type KeyValueDuration struct
{
	key_ string
	value_ interface{}
	duration_ time.Duration
}

type ValueDuration struct
{
	value_ interface{}
	duration_ time.Duration
}

type ValueResult struct
{
	value_ interface{}
	result_ bool
}

type CustomOperation struct
{
	cmd_ string
	data_ interface{}
}

type CustomCache struct {
	// 缓存最大大小
	max_size_ int64 
	// 当前缓存大小
	cur_size_ int64
	// 操作channel
	operation_ chan CustomOperation
	// 结果channel
	result_ chan interface{}
	// 数据
	data_ map[string]ValueDuration
	// 是否退出缓存
	exit_ bool
}

func (c *CustomCache) Init() int {
	c.max_size_ = 1024 * 1024
	c.cur_size_ = 20
	c.operation_ = make(chan CustomOperation)
	c.result_ = make(chan interface{})
	c.data_ = make(map[string]ValueDuration)
	c.exit_ = false
	// 启动操作
	routine_num := 0
	go CacheRun(c)
	routine_num += 1
	go ExpireCheckRun(c)
	routine_num += 1
	return routine_num
}

func (c *CustomCache) Exit() {
	c.exit_ = true
}

func (c *CustomCache) CalculateSize(key string, value interface{}, 
	duration time.Duration) int64 {
	size := 6 * (unsafe.Sizeof(key) + unsafe.Sizeof(value) + unsafe.Sizeof(duration))
	return int64(size)
}

func (c *CustomCache) SetMaxMemory(size string) bool {
	// fmt.Printf("SetMaxMemory %s\n", size)
	op := CustomOperation{"SetMaxMemory", size}
	c.operation_ <- op
	result := <-c.result_
	// fmt.Printf("SetMaxMemory result %t\n", result.(bool))
	return result.(bool)
}
func (c *CustomCache) SetMaxMemoryAux(size string) bool {
	// fmt.Printf("SetMaxMemoryAux %s\n", size)
	old_max_size := c.max_size_
	if len(size) < 3 {
		return false
	}
	num_str := size[0 : len(size)-2]
	unit_str := size[len(size)-2:]
	num, err := strconv.Atoi(num_str);
	if err != nil {
		return false
	}
	if unit_str == "KB" {
		c.max_size_ = int64(num * 1024)
	} else if unit_str == "MB" {
		c.max_size_ = int64(num * 1024 * 1024)
	} else if unit_str == "GB" {
		c.max_size_ = int64(num * 1024 * 1024 * 1024)
	} else {
		return false
	}
	if c.max_size_ < c.cur_size_ {
		c.max_size_ = old_max_size
		return false
	}
	// 最大2G
	if c.max_size_ > 2 * 1024 * 1024 * 1024 {
		c.max_size_ = old_max_size
		return false
	}
	return true
}

func (c *CustomCache)Set(key string, val interface{}, expire time.Duration) bool {
	// fmt.Println("Set key:", key, val, expire)
	op := CustomOperation{"Set", KeyValueDuration{key, val, expire}}
	c.operation_ <- op
	result := <-c.result_
	return result.(bool)
}
func (c *CustomCache)SetAux(key string, val interface{}, expire time.Duration) bool {
	size :=c.CalculateSize(key, val, expire)
	if c.cur_size_ + size > c.max_size_ {
		fmt.Println("cache is full.")
		return false
	}
	if expire > time.Duration(0) {
		expire = time.Duration(time.Now().UnixNano()) + expire
	}
	c.data_[key] = ValueDuration{val, expire}
	c.cur_size_ += size
	fmt.Println(c.cur_size_, c.max_size_)
	return true
}

func (c *CustomCache)Get(key string) (interface{}, bool) {
	// fmt.Printf("Get key:%s\n", key)
	op := CustomOperation{"Get", key}
	c.operation_ <- op
	result := <-c.result_
	return result.(ValueResult).value_,result.(ValueResult).result_
}
func (c *CustomCache)GetAux(key string) (interface{}, bool) {
	// fmt.Printf("GetAux key:%s\n", key)
	value_duration, exists := c.data_[key]
	var value interface{}
	if exists {
		value = value_duration.value_
	}
	return value, exists
}

func (c *CustomCache)Del(key string) bool {
	// fmt.Printf("Del key:%s\n", key)
	op := CustomOperation{"Del", key}
	c.operation_ <- op
	result := <-c.result_
	return result.(bool)
}
func (c *CustomCache)DelAux(key string) bool {
	value_duration, exists := c.data_[key]
	if !exists {
		return false
	}
	size := c.CalculateSize(key, value_duration.value_, value_duration.duration_)
	delete(c.data_, key)
	c.cur_size_ -= size
	return true
}

func (c *CustomCache)Exists(key string) bool {
	// fmt.Printf("Exists key:%s\n", key)
	op := CustomOperation{"Exists", key}
	c.operation_ <- op
	result := <-c.result_
	return result.(bool)
}
func (c *CustomCache)ExistsAux(key string) bool {
	_, exists := c.data_[key]
	if !exists {
		return false
	}
	return true
}

func (c *CustomCache)Flush() bool{
	// fmt.Printf("Flush\n")
	op := CustomOperation{"Flush", 0}
	c.operation_ <- op
	result := <-c.result_
	return result.(bool)
}
func (c *CustomCache)FlushAux() bool{
	// fmt.Printf("FlushAux\n")
	c.data_ = make(map[string]ValueDuration)
	c.cur_size_ = 20
	return true
}

func (c *CustomCache)Keys() int64 {
	// fmt.Printf("Keys\n")
	op := CustomOperation{"Keys", 0}
	c.operation_ <- op
	result := <-c.result_
	return result.(int64)
}
func (c *CustomCache)KeysAux() int64 {
	return int64(len(c.data_))
}

func (c *CustomCache)ExpireCheck() {
	// fmt.Printf("ExpireCheck\n")
	op := CustomOperation{"ExpireCheck", 0}
	c.operation_ <- op
	<-c.result_
}
func (c *CustomCache)ExpireCheckAux() {
	now := time.Duration(time.Now().UnixNano())
	want_delete := make([]string, 0)
	for key, value_duration := range c.data_ {
		if value_duration.duration_ == time.Duration(0) {
			continue
		}
		if now > value_duration.duration_ {
			want_delete = append(want_delete, key)
		}
	}
	for _, value := range want_delete {
		size := c.CalculateSize(value, c.data_[value].value_, c.data_[value].duration_)
		delete(c.data_, value)
		c.cur_size_ -= size
	}
}

func (c *CustomCache)Print() {
	// fmt.Printf("Print\n")
	op := CustomOperation{"Print", 0}
	c.operation_ <- op
	<-c.result_
}
func (c *CustomCache)PrintAux() {
	fmt.Println("====== PrintAux ========")
	fmt.Printf("max_size_:%d\n", c.max_size_)
	fmt.Printf("cur_size_:%d\n", c.cur_size_)
	for key, value_duration := range c.data_ {
		fmt.Println(key, value_duration.value_, value_duration.duration_)
	}
	fmt.Println("====== PrintAux end ====")
}

func CacheRun(c *CustomCache) {
	defer wg.Done()
	for !c.exit_ {
		op := <-c.operation_
		// fmt.Printf("CacheRun cmd %s\n", op.cmd_)
		if op.cmd_ == "SetMaxMemory" {
			result := c.SetMaxMemoryAux(op.data_.(string))
			c.result_ <- result
		} else if op.cmd_ == "Set" {
			data := op.data_.(KeyValueDuration)
			result := c.SetAux(data.key_, data.value_, data.duration_)
			c.result_ <- result
		} else if op.cmd_ == "Get" {
			value, exists := c.GetAux(op.data_.(string)) 
			c.result_ <- ValueResult{value, exists}
		} else if op.cmd_ == "Del" {
			result := c.DelAux(op.data_.(string))
			c.result_ <- result
		} else if op.cmd_ == "Exists" {
			result := c.ExistsAux(op.data_.(string))
			c.result_ <- result
		} else if op.cmd_ == "Flush" {
			result := c.FlushAux()
			c.result_ <- result
		} else if op.cmd_ == "Keys" {
			result := c.KeysAux()
			c.result_ <- result
		} else if op.cmd_ == "ExpireCheck" {
			c.ExpireCheckAux()
			c.result_ <- 0
		} else if op.cmd_ == "Print" {
			c.PrintAux()
			c.result_ <- 0
		}
	}
}

func ExpireCheckRun(c *CustomCache) {
	defer wg.Done()
	for !c.exit_ {
		c.ExpireCheck()
		time.Sleep(1 * time.Second)
	}
}

func main()  {
	fmt.Println("start main")
	cache := CustomCache{}
	routine_num := cache.Init()
	wg.Add(routine_num)

	// 测试填充1KB
	cache.SetMaxMemory("1KB")
	for i := 0; i < 10000000000; i++ {
		i_str := strconv.Itoa(i)
		result := cache.Set("name"+i_str, "yangdian"+i_str, 5*time.Second)
		if !result {
			break
		}
	}
	fmt.Println(cache.Keys())
	cache.Flush()
	cache.Print()
	// 测试100KB
	cache.SetMaxMemory("100KB")
	for i := 0; i < 10000000000; i++ {
		i_str := strconv.Itoa(i)
		result := cache.Set("name"+i_str, "yangdian"+i_str, 5*time.Second)
		if !result {
			break
		}
	}
	fmt.Println(cache.Keys())
	cache.Flush()
	cache.Print()
	// 测试1MB
	cache.SetMaxMemory("1MB")
	for i := 0; i < 10000000000; i++ {
		i_str := strconv.Itoa(i)
		result := cache.Set("name"+i_str, "yangdian"+i_str, 5*time.Second)
		if !result {
			break
		}
	}
	fmt.Println(cache.Keys())
	cache.Flush()
	cache.Print()
	// 测试2MB
	cache.SetMaxMemory("2MB")
	for i := 0; i < 10000000000; i++ {
		i_str := strconv.Itoa(i)
		result := cache.Set("name"+i_str, "yangdian"+i_str, 5*time.Second)
		if !result {
			break
		}
	}
	fmt.Println(cache.Keys())
	cache.Flush()
	cache.Print()
	// 测试1GB
	// cache.SetMaxMemory("1GB")
	// for i := 0; i < 10000000000; i++ {
	// 	i_str := strconv.Itoa(i)
	// 	result := cache.Set("name"+i_str, "yangdian"+i_str, 0)
	// 	if !result {
	// 		break
	// 	}
	// }
	// fmt.Println(cache.Keys())
	// cache.Flush()
	// cache.Print()
	// 测试删除
	cache.Set("name", "yangdian", 5*time.Second)
	cache.Set("aaa", 1, 6*time.Second)
	cache.Set("bbb", 1.1, 7*time.Second)
	value, result := cache.Get("name")
	fmt.Println(value, result)
	value, result = cache.Get("aaa")
	fmt.Println(value, result)
	value, result = cache.Get("bbb")
	fmt.Println(value, result)
	result = cache.Exists("name")
	fmt.Println(result)
	result = cache.Del("name")
	fmt.Println(result)
	result = cache.Del("aaa")
	fmt.Println(result)
	result = cache.Del("bbb")
	fmt.Println(result)
	cache.Print()

	// 测试并发安全
	wg.Add(2)
	cache.SetMaxMemory("1MB")
	go func() {
        defer wg.Done()
        for i := 0; i < 10000000000; i++ {
			i_str := strconv.Itoa(i)
			result := cache.Set("name"+i_str, "yangdian"+i_str, 5*time.Second)
			if !result {
				break
			}
		}
		fmt.Println(cache.Keys())
	}()
	go func() {
        defer wg.Done()
        for i := 0; i < 10000000000; i++ {
			i_str := strconv.Itoa(i)
			result := cache.Set("name"+i_str, "yangdian"+i_str, 5*time.Second)
			if !result {
				break
			}
		}
		fmt.Println(cache.Keys())
    }()

	// 测试过期时间
	cache.Set("name", "yangdian", 5*time.Second)
	cache.Set("aaa", 1, 6*time.Second)
	cache.Set("bbb", 1.1, 7*time.Second)
	cache.Print()
	time.Sleep(6 * time.Second)
	cache.Print()

	// cache.Exit()
	wg.Wait()
}
